//
// Created by feng on 19-3-28.
// PS: Define the heliostat data structure.
//

#include "CudaHeliostat.cuh"
#include <algorithm/rayTracing/qmcrt_util/global_function.cuh>
#include <algorithm/shared/tool/randomNumberGenerator/RandomGenerator.cuh>

using namespace solar::cuda;

/**
 * Calculate the normal of the heliostat.
 * @param focus_center : the center of the belonging receiver
 * @param sunray_dir : the sun ray direction after normalized
 */
/*
    shift_ ???
*/
void CudaHeliostat::CSetNormal(const float3& focus_center, const float3& sunray_dir)
{
    float3 local_center = make_float3(pos_.x, pos_.y, pos_.z); // 定日镜中心
    float3 focus_centers =
        make_float3(focus_center.x, focus_center.y + shift_, focus_center.z); // 接收器中心，shift_?

    float3 reflect_dir1 = focus_centers - local_center; // 反射方向
    float3 reflect_dir = normalize(reflect_dir1);
    normal_ = normalize(reflect_dir - sunray_dir); // 计算得到归一化的法向
}

/**
 * Transform the local vertexs to world coordinate system.
 */
void CudaHeliostat::CSetWorldVertex()
{
    vertex_[0] = make_float3(-size_.x / 2, size_.y / 2, -size_.z / 2);
    vertex_[1] = make_float3(0, 0, size_.z) + vertex_[0];
    vertex_[2] = make_float3(size_.x, 0, size_.z) + vertex_[0];
    vertex_[3] = make_float3(size_.x, 0, 0) + vertex_[0];
    // First step: rotate
    vertex_[0] = global_func::local2world_rotate(vertex_[0], normal_);
    vertex_[1] = global_func::local2world_rotate(vertex_[1], normal_);
    vertex_[2] = global_func::local2world_rotate(vertex_[2], normal_);
    vertex_[3] = global_func::local2world_rotate(vertex_[3], normal_);
    // Second step: translate
    vertex_[0] = global_func::translate(vertex_[0], pos_);
    vertex_[1] = global_func::translate(vertex_[1], pos_);
    vertex_[2] = global_func::translate(vertex_[2], pos_);
    vertex_[3] = global_func::translate(vertex_[3], pos_);
}

/**
 * 1) Set normal
 * 2)change vertexes from local to world
 * @param focus_center
 * @param sunray_dir
 */
void CudaHeliostat::CSetNormalAndRotate(const float3& focus_center, const float3& sunray_dir)
{
    CSetNormal(focus_center, sunray_dir);
    CSetWorldVertex();
}

/**
 *
 */
auto CudaHeliostat::generateDeviceMicrohelioGroup(int num_group, int size) -> int*
{
    int* d_groups = nullptr;
    checkCudaErrors(cudaMalloc((void**)&d_groups, sizeof(int) * size));
    // printf("%d\n",size);
    RandomGenerator::gpuUniform(d_groups, 0, num_group, size, stream_);
    return d_groups;
}

/**
 * Getters and setters of attributes for heliostat object.
 */
auto CudaHeliostat::getPosition() const -> float3 { return pos_; }

void CudaHeliostat::setPosition(float3 pos) { pos_ = pos; }
auto CudaHeliostat::getShift() const -> float { return shift_; }

void CudaHeliostat::setShift(float shift) { shift_ = shift; }
auto CudaHeliostat::getSize() const -> float3 { return size_; }
void CudaHeliostat::setSize(float3 size) { this->size_ = size; }
void CudaHeliostat::setSizeX(float x) { this->size_.x = x; }
void CudaHeliostat::setSizeY(float y) { this->size_.y = y; }
void CudaHeliostat::setSizeZ(float z) { this->size_.z = z; }

auto CudaHeliostat::getNormal() const -> float3 { return normal_; }

void CudaHeliostat::setNormal(float3 normal) { normal_ = normal; }

auto CudaHeliostat::getRowAndColumn() const -> int2 { return row_col_; }

void CudaHeliostat::setRowAndColumn(int2 row_col) { row_col_ = row_col; }

auto CudaHeliostat::getGap() const -> float2 { return gap_; }

void CudaHeliostat::setGap(float2 gap) { gap_ = gap; }

auto CudaHeliostat::getSubCenterType() const -> SubCenterType { return subCenterType_; }

void CudaHeliostat::setSubCenterType(SubCenterType type) { subCenterType_ = type; }

auto CudaHeliostat::getPixelLength() const -> float { return pixel_length_; }

void CudaHeliostat::setPixelLength(float pixel_length) { pixel_length_ = pixel_length; }

auto CudaHeliostat::getBelongingGridId() const -> int { return belonging_grid_id_; }

void CudaHeliostat::setBelongingGridId(int belonging_grid_id)
{
    belonging_grid_id_ = belonging_grid_id;
}

auto CudaHeliostat::getCPULocalNormals() -> std::vector<float3>
{
    std::vector<float3> cpu_local_normals;
    for (int i = 0; i < row_col_.x * row_col_.y; ++i)
    {
        cpu_local_normals.emplace_back(h_local_normals[i]);
    }
    return cpu_local_normals;
}

void CudaHeliostat::setCPULocalNormals(float3* h_local_normals_)
{
    h_local_normals = h_local_normals_;
}

void CudaHeliostat::setCPULocalNormals(std::vector<float3> local_normals)
{
    int size = local_normals.size();
    h_local_normals = new float3[size];
    for (int i = 0; i < size; ++i)
    {
        h_local_normals[i] = local_normals[i];
    }
}

auto CudaHeliostat::getCPULocalCenters() -> std::vector<float3>
{
    std::vector<float3> cpu_local_centers;
    for (int i = 0; i < row_col_.x * row_col_.y; ++i)
    {
        //        printf("h_local_centers[%d] = (%f,%f,%f)\n",i,h_local_centers[i].x,
        //        h_local_centers[i].y, h_local_centers[i].z);
        cpu_local_centers.push_back(h_local_centers[i]);
    }
    return cpu_local_centers;
}

void CudaHeliostat::setCPULocalCenters(float3* h_local_centers_)
{
    //    printf("h_local_centers_ %f %f %f
    //    \n",h_local_centers_[0].x,h_local_centers_[0].y,h_local_centers_[0].z);
    h_local_centers = h_local_centers_;
}

void CudaHeliostat::setCPULocalCenters(std::vector<float3> local_centers)
{
    int size = local_centers.size();
    h_local_centers = new float3[size];
    for (int i = 0; i < size; ++i)
    {
        h_local_centers[i] = local_centers[i];
    }
}

auto CudaHeliostat::getHeliostatDepth() const -> float { return helio_depth_; }
auto CudaHeliostat::getHeliostatrefract() const -> float { return refract_; }
auto CudaHeliostat::getHeliostatreflect() const -> float
{
    return std::pow((refract_ - 1) / (refract_ + 1), 2);
}

void CudaHeliostat::setRefract(float refract) { this->refract_ = refract; }

auto CudaHeliostat::getRefract() const -> float { return refract_; }

void CudaHeliostat::setHelioDepth(float thickness) { helio_depth_ = thickness; }

auto CudaHeliostat::getHelioDepth() const -> float { return helio_depth_; }
